//
//  VHLSQLiteObject.swift
//  VideoEditor_Swift
//
//  Created by Vincent on 2020/5/25.
//  Copyright © 2020 Darnel Studio. All rights reserved.
//

import UIKit

/**
 Tips
 实现 Codable 协议，用于 json to model
 日期格式存储自动转为 yyyy-MM-dd HH:mm:ss 格式
 
 
 新加字段必须声明为可选性，避免无法对不存在的字段进行 Codable。最好所有字段都加上可选型
 扩展协议实现时，需要在声明对象时标记 Codable 协议
 */

/// 数据库存储 ORM 对象
public protocol VHLSQLiteObject: Codable {
    var pkid: Int? { get set }
    var tableName: String { get }
    
    // ** 协议必须实现 init 方法，用于通过类对象获取属性 **
    init()
    
    /// 主键字段
    func primaryKey() -> String // "pkid"
    
    /// 唯一的字段，如果不存在，就需要先使用数据查询到主键以后才能更新，删除，移除
    func uniqueKeys() -> [String]?
    
    /// 忽略的字段，不保存
    func ignoreKeys() -> [String]?
}

public extension VHLSQLiteObject {
    /// 获取类名
    var tableName: String {
        get {
            return String(describing: type(of: self))
        }
    }
    init() { self.init() }
    /// 主键
    func primaryKey() -> String { return "pkid" }
    func uniqueKeys() -> [String]? { return nil }
    func ignoreKeys() -> [String]? { return ["tableName"] }
}

// MARK: - 获取对象属性
public extension VHLSQLiteObject {
    var colums: [String] { get { return getProperties().map { $0.key } } }

    //MARK: 通过反射获取对应的属性列表
    func getProperties() -> [VHLSQLiteProperty] {
        let mirror = Mirror(reflecting: self)
        /// 判断支持的类型
        guard let displayStyle = mirror.displayStyle else { return [] }
        guard displayStyle == .class || displayStyle == .struct else {
            VHLSQLitePrint("只支持 class 或者 struct 作为 Model")
            return []
        }
        
        // 获取 children 属性
        var children = [(label: String?, value: Any)]()
        children += mirror.children

        var currentMirror = mirror
        while let superclassChildren = currentMirror.superclassMirror?.children {
            children += superclassChildren
            currentMirror = currentMirror.superclassMirror!
        }
        var result = [(String, Any)]()
        children.forEach { (child) in
            if let _label = child.label {
                result.append((_label, child.value))
            }
        }
        
        guard children.count > 0 else { return [] }
        
        var properties: [VHLSQLiteProperty] = []
        for child in children {
            let value = child.value
            let vMirror = Mirror(reflecting: value)  // 通过值来创建属性的反射
            
            if let name = child.label {  //注意字典只能保存AnyObject类型。
                /// 忽略表名
                if name == "tableName" { continue }
                /// 忽略懒加载的属性
                if name.hasPrefix("$__lazy_storage_$_") || name.hasSuffix(".storage") { continue }
                /// 判断是否是忽略的 key
                if let ignoreKeys = ignoreKeys(), ignoreKeys.contains(name) {
                    continue
                }
                
                let isPrimaryKey = (primaryKey() == name)
                
                let property = VHLSQLiteProperty(type: vMirror.subjectType, key: name, value: value as AnyObject, isPrimaryKey: isPrimaryKey)
                properties.append(property)
            }
        }
        
        return properties
    }
}

// MARK: - 扩展 表操作 方法
public extension VHLSQLiteObject {
    // MARK: 表是否存在
    func isTableExists(_ db: VHLSQLiteDataBase = .shared) -> Bool {
        return db.tableExists(tableName: self.tableName)
    }
    
    // MARK: 当前对象是否存在
    func isExists(_ db: VHLSQLiteDataBase = .shared) -> Bool {
        return db.objectExists(self)
    }
    
    // MARK: 创建表
    @discardableResult
    func createTable(_ db: VHLSQLiteDataBase = .shared) throws -> Bool {
        guard self.tableName != "" else {
            VHLSQLitePrint(String(describing: type(of: self)), "表名不存在")
            return false
        }
        return try db.createTable(object: self)
    }
    
    // MARK: 清空表
    @discardableResult
    func cleanTable(_ db: VHLSQLiteDataBase = .shared) throws -> Bool {
        return try db.cleanTable(self.tableName)
    }
    
    // MARK: 删除表
    @discardableResult
    func dropTable(_ db: VHLSQLiteDataBase = .shared) throws -> Bool {
        return try db.dropTable(self.tableName)
    }
}

// MARK: - 扩展 增删改查 方法
public extension VHLSQLiteObject {
    // MARK: - 只保存（减少查询是否存在）
    @discardableResult
    func save(_ db: VHLSQLiteDataBase = .shared) throws -> Bool {
        return try db.save(self)
    }
    
    // MARK: 保存或者修改
    @discardableResult
    func saveOrUpdate(_ db: VHLSQLiteDataBase = .shared) throws -> Bool {
        return try db.saveOrUpdate(self)
    }
    
    // MARK: 删除
    @discardableResult
    func delete(_ db: VHLSQLiteDataBase = .shared) throws -> Bool {
        return try db.delete(self)
    }
    
    // MARK: 修改指定属性
    @discardableResult
    func update(_ db: VHLSQLiteDataBase = .shared, parameters: [String: Any]) throws -> Int {
        if parameters.count <= 0 { return -1 }
        
        let whereSQL = whereSelfSQL()
        let (sql, params) = updateSQLPrames(parameters: parameters, whereSQL: whereSQL)
        if sql.isEmpty { return -1 }

        return try db.execute(sql, params: params)
    }
    
    @discardableResult
    func update(_ db: VHLSQLiteDataBase = .shared, propertyNames: [String]) throws -> Int {
        var parameters: [String: Any] = [:]
        let properties = getProperties()
        for property in properties {
            if !propertyNames.contains(property.key) { continue }
            if VHLSQLiteTool.valueIsNil(property.value) { continue }
            
            parameters[property.key] = property.value
        }
        if parameters.count <= 0 { return -1 }
        return try update(parameters: parameters)
    }
}

// MARK: - 类对象操作 增删改查
public extension VHLSQLiteObject {
    // MARK: 查询
    static func rows(_ db: VHLSQLiteDataBase = .shared, for sql: String) throws -> [Self] {
        let object = Self.init()
        
        try db.createTable(object: object)
        let results = try db.query(sql)
        
        let objects = try objectsWithResults(results)
        return objects
    }
    
    static func rows(_ db: VHLSQLiteDataBase = .shared,
                     filter: String = "",
                     order: VHLSQLiteOrderBy? = nil,
                     limit: Int = 0) throws -> [Self] {
        let object = Self.init()
        let tableName = object.tableName
        
        var sql = "SELECT * FROM \(tableName)"
        
        // WHERE
        if !filter.isEmpty {
            sql += " WHERE \(filter)"
        }
        
        // Order
        if let order = order {
            if object.colums.contains(order.keyName) {
                sql += " ORDER BY \(order.sql)"
            }
        }
        
        // Limit
        if limit > 0 {
            sql += " LIMIT 0, \(limit)"
        }
        return try rows(db, for: sql)
    }
    
    /// 查询。可以通过参数的形式指定查询条件
    static func rows(_ db: VHLSQLiteDataBase = .shared,
                     filter parameters: [String: Any],
                     order: VHLSQLiteOrderBy? = nil,
                     limit: Int = 0) throws -> [Self] {
        let object = Self.init()
        let tableName = object.tableName
        var sql = "SELECT * FROM \(tableName)"
        
        // WHERE
        let objectProperties = object.getProperties()
        let objectPropertyKeys = objectProperties.map({ $0.key })
        let parameters = parameters.filter({ objectPropertyKeys.contains($0.key) })

        var whereSQL = ""
        var params: [Any] = []
        var isFirst = true
        
        for parameter in parameters {
            let key = parameter.key
            let value = parameter.value
            
            if VHLSQLiteTool.valueIsNil(value) { continue }
            
            whereSQL += isFirst ? "`\(key)` = ?" : ", `\(key)` = ?"
            params.append(value as Any)

            isFirst = false
        }
        
        if params.count > 0 {
            sql += " WHERE \(whereSQL)"
        }
        
        // Order
        if let order = order {
            if object.colums.contains(order.keyName) {
                sql += " ORDER BY \(order.sql)"
            }
        }
        
        // Limit
        if limit > 0 {
            sql += " LIMIT 0, \(limit)"
        }

        try db.createTable(object: object)
        
        let results = try db.query(sql, params: params)
        let objects = try objectsWithResults(results)
        return objects
    }
    
    /// 修改指定属性
    static func update(_ db: VHLSQLiteDataBase = .shared, parameters: [String: Any], whereSQL: String) throws -> Int {
        let object = Self.init()
        let (sql, params) = object.updateSQLPrames(parameters: parameters, whereSQL: whereSQL)
        if sql.isEmpty { return -1 }
        
        return try db.execute(sql, params: params)
    }
    /// 根据条件删除
    static func delete(_ db: VHLSQLiteDataBase = .shared, whereSQL: String) throws -> Bool {
        let object = Self.init()
        let tableName = object.tableName
        var sql = "DELETE FROM \(tableName)"

        if !whereSQL.isEmpty {
            sql += (whereSQL.contains("WHERE") ? " " : " WHERE ") + whereSQL
        }
        return try db.execute(sql) > 0
    }
}

// MARK: - 序列化
extension VHLSQLiteObject {
    static func objectsWithResults(_ results: [[String: Any]]) throws -> [Self] {
        let object = Self.init()

        let properties = object.getProperties()
        let colums = object.colums
        
        var objects: [Self] = []
        
        for result in results {
            var keyValues = result.filter { (dict) -> Bool in
                return colums.contains(dict.key)
            }
            // 处理类型不匹配
            for (key, value) in keyValues {
                if let property = properties.first(where: { $0.key == key }) {
                    keyValues[key] = property.transformValueType(value)
                }
            }
            
            if let object = try objectWithResult(keyValues) {
                objects.append(object)
            }
        }
        
        return objects
    }
    static func objectWithResult(_ result: [String: Any]) throws -> Self? {
        // ** OC 对象赋值的方式 **
//        let object = Self.init()
//        if object is NSObject {
//            for (key, value) in result {
//                // 需要对象设置为 @objc
//                (object as? NSObject)?.setValue(value, forKey: key)
//            }
//            return object
//        }
        
        // ** Codable 的方式 **
        do {
            // 如果 result 中包含有不能 Json 化的对象会抛出异常
            var xResult = result
            for (key, value) in result {
                /// 处理 JSONSerialization 不能格式化的类型。
                /// Codable 会将 base64 的字符串对应为 data 类型
                /// 分配的内存过大会导致闪退   unable to allocate memory for length
                if let value = value as? Data {
                    xResult[key] = value.base64EncodedString()
                }
            }
                
            let data = try JSONSerialization.data(withJSONObject: xResult, options: .fragmentsAllowed)
            
            let decoder = JSONDecoder()
            // 日期格式化
            decoder.dateDecodingStrategy = .formatted(VHLSQLiteTool.dateFormatter)
            let decoderObject = try decoder.decode(Self.self, from: data)
            return decoderObject
        } catch {
            DPrint("VHLSQLite 字典转对象错误: \(error)")
            throw error
        }
    }
}

// MARK: - 序列化
public extension VHLSQLiteObject {
    func toDict() throws -> [String: Any] {
        let data = try JSONEncoder().encode(self)
        guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else {
            throw NSError()
        }
        return dictionary
    }
    
    func toJSON() throws -> String {
        let data = try JSONEncoder().encode(self)
        return String(data: data, encoding: .utf8) ?? ""
    }
}

// MARK: 扩展数组操作
public extension Array where Element: VHLSQLiteObject {
    func saveOrUpdate(db: VHLSQLiteDataBase? = nil) throws {
        let database = db ?? VHLSQLiteDataBase.shared
        try database.transaction {
            for element in self {
                _ = try element.saveOrUpdate()
            }
        }
    }
}

/**
 使用教程
 1. model 继承自 VHLSQLiteObject
 2. 自定义实现 codable 协议
 */
